<?php 

function array_transpose($array, $selectKey = false) { 
    if (!is_array($array)) return false; 
    $return = array(); 
    foreach($array as $key => $value) { 
        if (!is_array($value)) return $array; 
        if ($selectKey) { 
            if (isset($value[$selectKey])) $return[] = $value[$selectKey]; 
        } else { 
            foreach ($value as $key2 => $value2) { 
                $return[$key2][$key] = $value2; 
            } 
        } 
    } 
    return $return; 
}

function cmpBySum($pA, $pB) {
    $lA = array_sum($pA);
    $lB = array_sum($pB);
	if ($lA == $lB) {
        return 0;
    }
    return ($lA > $lB) ? -1 : 1;
}

class WarsowAnalyzer {

	protected $mEvents;
	protected $mTeams;
	protected $mKills;
	protected $mOutputBase = 'stats';
	protected $mGameID;
	protected $mDB;

	function getPlayerName($pString) {
		return preg_replace('/\^[0-9]/', '', $pString);
	}

	function getActorName($pString) {
		return getPlayerName(substr($pString, 0, strpos($pString, '^7 ')));
	}

	function getSubjectName($pString) {
		$lPos = strpos($pString, 'by') + 3;
		return getPlayerName(substr($pString, $lPos, strpos($pString, '^7', $lPos + 1) - $lPos));
	}

	function getTeamName($pString) {
		$lPos = strpos($pString, 'joined the ') + 11;
		return substr($pString, $lPos, strpos($pString, ' ', $lPos + 1) - $lPos);
	}

	function captureEvent($pText, $pRegex1, &$pActor1, $pRegex2 = '', &$pActor2 = '') {
		if (!preg_match($pRegex1, $pText, $lMatches)) {
			return false;
		}
		$pActor1 = $lMatches[1];
		if (strlen($pRegex2) == 0) {
			return true;
		}
		if (!preg_match($pRegex2, $pText, $lMatches)) {
			return false;
		}
		$pActor2 = $lMatches[1];
		return true;
	}

	function logEvent($pDate, $pType, $pActor = '', $pSubject = '') {
		/*$lQuery = sprintf("INSERT INTO event (matchID, type ,actor ,subject, date) VALUES ('%s', '%s', '%s', '%s', '%s');", $this->mGameID, $pType, $pActor, $pSubject, $pDate);
		$this->mDB->query($lQuery);*/
	}

	function addKill($pDate, $pActor, $pSubject) {
		if (!isset($this->mKills[$pSubject])) {
			$this->mKills[$pSubject] = array();
		}
		if (!isset($this->mKills[$pSubject][$pActor])) {
			$this->mKills[$pSubject][$pActor] = 0;
		}
		$this->mKills[$pSubject][$pActor]++;
		$this->logEvent($pDate, 'kill', $pActor, $pSubject);
	}

	function setTeam($pDate, $pActor, $pTeam) {
		$this->mTeams[$pActor] = $pTeam;
		$this->logEvent($pDate, 'team', $pActor, $pTeam);
	}

	function addEvent($pDate, $pEvent, $pActor, $pSubject) {
		if (!isset($this->mEvents[$pEvent])) {
			$this->mEvents[$pEvent] = array();
		}
		if (!isset($this->mEvents[$pEvent][$pActor])) {
			$this->mEvents[$pEvent][$pActor] = 0;
		}
		$this->mEvents[$pEvent][$pActor]++;
		$this->logEvent($pDate, $pEvent, $pActor, $pSubject);
	}

	function writeStats($pMatchType, $pOutputFilename, $lMatchDate, $lAlphaScore, $lBetaScore) {
		$lOutput = '';
		$lOutput .= '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">'."\n";
		$lOutput .= '<html>'."\n";
		$lOutput .= '	<head>'."\n";
		$lOutput .= '		<link rel="stylesheet" href="style.css" type="text/css" media="screen"/>'."\n";
		$lOutput .= '	</head>'."\n";
		$lOutput .= '<body>'."\n";
		$lOutput .= "<h1>Match statistics ($pMatchType/$lMatchDate)</h1>"."\n";
		$lOutput .= "<div class=\"left\">"."\n";
		$lOutput .= "<h2>Teams</h2>"."\n";
		$lOutput .= "<div class=\"team-ALPHA\">"."\n";
		$lOutput .= "<h3>Alpha ($lAlphaScore)</h3>"."\n";
		foreach ($this->mTeams as $lActor => $lTeam) {
			if ($lTeam == 'ALPHA') {
				$lScore = 0;
				if (isset($this->mEvents['Flag Capture!'][$lActor]))
				{
					$lScore = $this->mEvents['Flag Capture!'][$lActor];
				}
				$lScore = isset($lScore)?' ('.$lScore.')':'';
				$lOutput .= $lActor.$lScore."<br/>"."\n";
			}
		}
		$lOutput .= "</div>"."\n";
		$lOutput .= "<div class=\"team-BETA\">"."\n";
		$lOutput .= "<h3>Beta ($lBetaScore)</h3>"."\n";
		foreach ($this->mTeams as $lActor => $lTeam) {
			if ($lTeam == 'BETA') {
				$lScore = 0;
				if (isset($this->mEvents['Flag Capture!'][$lActor]))
				{
					$lScore = $this->mEvents['Flag Capture!'][$lActor];
				}
				$lScore = isset($lScore)?' ('.$lScore.')':'';
				$lOutput .= $lActor.$lScore."<br/>"."\n";
			}
		}
		$lOutput .= "</div>"."\n";
		$lOutput .= "<br class=\"clear\"/>"."\n";
		$lOutput .= "</div>"."\n";
		$lOutput .= "<div class=\"right\">"."\n";
		$lOutput .= "<h2>Events</h2>"."\n";
		$lOutput .= "<table border='1'><tr><th>Event</th><th># (total)</th><th>top player</th><th># (top player)</th></tr>"."\n";
		//print_r($this->mEvents);
		ksort($this->mEvents);
		foreach ($this->mEvents as $lEvent => $lActors) {
			$lOutput .= "<tr><td>".$lEvent."</td>"."\n";
			$lOutput .= "<td>".array_sum($lActors)."</td>"."\n";
			arsort($lActors);
			$lValues = array_keys($lActors);
			$lActor = $lValues[0];
			$lOutput .= "<td class=\"team-".$this->mTeams[$lActor]."\">".$lActor."</td><td class=\"team-".$this->mTeams[$lActor]."\">".$lActors[$lValues[0]]."</td>"."\n";
			$lOutput .= "</tr>"."\n";
		}
		$lOutput .= "</table>"."\n";
		$lOutput .= "</div>"."\n";
		$lOutput .= '<br class="clear"/>'."\n";		
		$lOutput .= "<div class=\"left\">"."\n";
		$lOutput .= "<h2>Kills</h2>"."\n";
		$lOutput .= "<table border='1'><tr><th>Player</th><th># (total)</th><th>favorite target</th><th># (favorite target)</th></tr>"."\n";
		uasort($this->mKills, 'cmpBySum');
		foreach ($this->mKills as $lActor => $lSubjects) {
			$lOutput .= "<tr class=\"team-".(isset($this->mTeams[$lActor])?$this->mTeams[$lActor]:'')."\"><td>".$lActor."</td>"."\n";
			if (!isset($lSubjects)) {
				$lOutput .= "<td>0</td><td>killed nobody</td><td>0</td>"."\n";
				continue;
			}
			$lOutput .= "<td>".array_sum($lSubjects)."</td>"."\n";
			arsort($lSubjects);
			$lValues = array_keys($lSubjects);
			$lOutput .= "<td>".$lValues[0]."</td><td>".$lSubjects[$lValues[0]]."</td>"."\n";
			$lOutput .= "</tr>"."\n";
		}
		$lOutput .= "</table>"."\n";
		$lOutput .= "</div>"."\n";
		$lOutput .= "<div class=\"right\">"."\n";
		$lOutput .= "<h2>Killed</h2>"."\n";
		$lOutput .= "<table border='1'><tr><th>Player</th><th># (total)</th><th>nightmare</th><th># (nightmare)</th></tr>"."\n";
		$this->mKillsed = array_transpose($this->mKills);
		uasort($this->mKillsed, 'cmpBySum');
		foreach ($this->mKillsed as $lActor => $lSubjects) {
			$lOutput .= "<tr class=\"team-".(isset($this->mTeams[$lActor])?$this->mTeams[$lActor]:'')."\"><td>".$lActor."</td>"."\n";
			if (!isset($lSubjects)) {
				$lOutput .= "<td>0</td><td>not killed</td><td>0</td>"."\n";
				continue;
			}
			$lOutput .= "<td>".array_sum($lSubjects)."</td>"."\n";
			arsort($lSubjects);
			$lValues = array_keys($lSubjects);
			$lOutput .= "<td>".$lValues[0]."</td><td>".$lSubjects[$lValues[0]]."</td>"."\n";
			$lOutput .= "</tr>"."\n";
		}
		$lOutput .= "</table>"."\n";
		$lOutput .= "</div>"."\n";
		$lOutput .= "<br class=\"clear\"/>"."\n";
		$lOutput .= '</body>'."\n";
		$lOutput .= '</html>'."\n";
		
		$lOutputFile = @fopen($pOutputFilename, 'wt');

		if (!$lOutputFile) {
			die('No output file: "'.$pOutputFilename.'".');
		}
		fprintf($lOutputFile, $lOutput);
		fclose($lOutputFile);
	}

	function getOutputFilename($pMatchDate, $pPostFix = '') {
		if (!empty($pMatchDate)) {
			$lMatchDateArray = date_parse($pMatchDate);
			$lDisplayDate = sprintf("%04d%02d%02d", $lMatchDateArray['year'], $lMatchDateArray['month'], $lMatchDateArray['day']);
			$pPostFix = (empty($pPostFix) ? '' : '-'.$pPostFix);
			$lOutputFilename = $this->mOutputBase.'-'.$lDisplayDate.$pPostFix.'.html';
			/*$lID = 0;
			while (file_exists($lOutputFilename)) {
				$lOutputFilename = $this->mOutputBase.'-'.$lDisplayDate.$pPostFix.'-'.$lID.'.html';
				++$lID;
			}*/
		} else {
			$lOutputFilename = $this->mOutputBase.$pPostFix.'-'.$this->mGameID.'.html';
		}
		return $lOutputFilename;
	}

	function parseFile() {
		/*try {
			$this->mDB = new PDO('mysql:host=localhost;dbname=wswstats', 'wswstats', 'wswstats');
		} catch (PDOException $lException) {
			echo "Exception: ".$lException->getMessage()."\n";
		}*/

		$lLogIncompleteGames = true;

		$lInputFilename = 'wswconsole.log';
		$lInputFile = @fopen($lInputFilename, 'rt');
		
		if (!$lInputFile) {
			die('No input file: "'.$lInputFilename.'".');
		}
		
		$lTimeStart = time();

		$this->mGameID = 0;
		$lMatchStarted = false;
		$lNbLines = 0;
		while (!feof($lInputFile)) {
			$lLine = fgets($lInputFile);
			++$lNbLines;
			$lLogLine = '';
			$lDate = '';
			if (preg_match('/^([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z) (.*)$/', $lLine, $lMatches)) {
				$lDate = $lMatches[1];
				$lLogLine = $lMatches[2];
			} else /*if ($lMatchStarted)*/ {
				$lLogLine = $lLine;
				continue;
			}
			if (strlen($lLogLine) != 0) {
				if ($this->captureEvent($lLogLine, '/SpawnServer: (.*)/', $lMapName)) {
					$lPlayers = array();
					$this->mTeams = array();
					$this->mEvents = array();
					$this->mKills = array();

					$lMatchDate = $lDate;
					++$this->mGameID;
					$this->logEvent($lMatchDate, 'spawn', $lMapName);
				} else if (strpos($lLogLine, 'G_Shutdown') !== FALSE) {
					if ($lLogIncompleteGames) {
						if (!empty($this->mKills) && !empty($this->mEvents)) {
							if (!empty($lMatchDate) && !empty($lDate)) {
								echo "Warmup start: $lMatchDate end: $lDate\n";
							}
							/*if (!empty($lMatchDate) && !empty($lDate)) {
								$lMatchDateArray = date_parse($lMatchDate);
								//print_r($lMatchDateArray);
								$lMatchDateTS = mktime($lMatchDateArray['hour'], $lMatchDateArray['minute'], $lMatchDateArray['second'], $lMatchDateArray['month'], $lMatchDateArray['day'], $lMatchDateArray['year']);
								$lEndMatchDateArray = date_parse($lDate);
								//print_r($lEndMatchDateArray);
								$lEndMatchDateTS = mktime($lEndMatchDateArray['hour'], $lEndMatchDateArray['minute'], $lEndMatchDateArray['second'], $lEndMatchDateArray['month'], $lEndMatchDateArray['day'], $lEndMatchDateArray['year']);
								$lDuration = ($lEndMatchDateTS - $lMatchDateTS);
								echo (int)($lDuration / 60)."m ".($lDuration % 60)."s\n";
							}/**/
							$this->logEvent($lDate, 'end', 'warmup');

							$this->writeStats('Warmup/'.$lMapName, $this->getOutputFilename($lMatchDate, 'warmup'), '', 0, 0);
							//++$this->mGameID;
						}
						$lMatchDate = $lDate;
					}
				} else if (strpos($lLogLine, 'Match starting!') !== FALSE) {
					$this->mEvents = array();
					$this->mKills = array();
					$lMatchStarted = true;
					$lMatchDate = $lDate;
					$this->logEvent($lDate, 'starting');
				} else if ($this->captureEvent($lLogLine, '/(.*)\^7 joined the/', $lActor, '/joined the (.*) team./', $lTeam)) {
					$lTeam = $this->getPlayerName($lTeam);
					if ($lTeam != "SPECTATOR") {
						$lActor = $this->getPlayerName($lActor);
						$this->setTeam($lDate, $lActor, $lTeam);
					}
				} else if ($this->captureEvent($lLogLine, '/(.*)\^7 is ready/', $lActor)) {
					$lActor = $this->getPlayerName($lActor);
					$lPlayers[$lActor] = 1;
				} else if ($this->captureEvent($lLogLine, '/(.*) \^7was killed/', $lActor)) {
					$lActor = $this->getPlayerName($lActor);
					$this->addEvent($lDate, 'Suicide', $lActor, '');
				} else if ($this->captureEvent($lLogLine, '/(.*)\^7 was instagibbed/', $lActor, '/ was instagibbed by (.*)\^7\'s instabeam/', $lSubject)) {
					$lActor = $this->getPlayerName($lActor);
					$lSubject = $this->getPlayerName($lSubject);
					$this->addKill($lDate, $lActor, $lSubject);
				} else if ($this->captureEvent($lLogLine, '/(.*) receives a/', $lActor, '/receives a \'(.*)\' award./', $lAward)) {
					$lActor = $this->getPlayerName($lActor);
					$this->addEvent($lDate, $lAward, $lActor, '');
				} else if ($this->captureEvent($lLogLine, '/\^7ALPHA\^7 vs BETA\^7 - \^.([0-9]*) : /', $lAlphaScore, '/\^7ALPHA\^7 vs BETA\^7 - \^.[0-9]* : \^.([0-9]*)/', $lBetaScore)) {
					if (!empty($lMatchDate) && !empty($lDate)) {
						echo "Game start: $lMatchDate end: $lDate\n";
					}
					
					/*if (!empty($lMatchDate) && !empty($lDate)) {
						$lMatchDateArray = date_parse($lMatchDate);
						//print_r($lMatchDateArray);
						$lMatchDateTS = mktime($lMatchDateArray['hour'], $lMatchDateArray['minute'], $lMatchDateArray['second'], $lMatchDateArray['month'], $lMatchDateArray['day'], $lMatchDateArray['year']);
						$lEndMatchDateArray = date_parse($lDate);
						//print_r($lEndMatchDateArray);
						$lEndMatchDateTS = mktime($lEndMatchDateArray['hour'], $lEndMatchDateArray['minute'], $lEndMatchDateArray['second'], $lEndMatchDateArray['month'], $lEndMatchDateArray['day'], $lEndMatchDateArray['year']);
						$lDuration = ($lEndMatchDateTS - $lMatchDateTS);
						echo (int)($lDuration / 60)."m ".($lDuration % 60)."s\n";
					}/**/
					$this->logEvent($lDate, 'end', 'game');

					$this->writeStats('Game/'.$lMapName, $this->getOutputFilename($lMatchDate), $lMatchDate, $lAlphaScore, $lBetaScore);
					//++$this->mGameID;

					$lMatchStarted = false;
					$lMatchDate = $lDate;

					$lPlayers = array();
					$this->mTeams = array();
					$this->mEvents = array();
					$this->mKills = array();
				}
			}
		}
		$lTimeEnd = time();
		echo "Parsed: $lNbLines in ".($lTimeEnd - $lTimeStart)."s\n";
		
		fclose($lInputFile);
	}
}

$lAnalyzer = new WarsowAnalyzer();
$lAnalyzer->parseFile();

?>
